#include "utils/byte_queue.h"
#include "os/os.h"
#include "bsp/cpu_driver.h"
#include "utils/utils.h"
#include "utils/circle_buffer.h"
#include "communication/message.h"

#define UART_BAUDRATE				230400

#ifdef DEBUG_PORT_UART1
#define UART_com					USART1
#define UART_tx_port				GPIOA
#define UART_tx_pin				GPIO_PIN_2
#define UART_rx_port				GPIOA
#define UART_rx_pin				GPIO_PIN_3
#define UART_irq					USART1_IRQn
#define UART_clk					RCU_USART1
#define UART_tx_gpio_clk			RCU_GPIOA
#define UART_rx_gpio_clk			RCU_GPIOA
#define UART_tx_dma				DMA0
#define UART_tx_dma_ch			DMA_CH6
#define UART_tx_dma_clk			RCU_DMA0
#define UART_rx_dma				DMA0
#define UART_rx_dma_ch			DMA_CH5
#define UART_rx_dma_clk			RCU_DMA0
#define UART_DMA_TX_IRQ          DMA0_Channel6_IRQn
#define UART_DMA_IRQHandler      DMA0_Channel6_IRQHandler
#else
#define UART_com					USART2
#define UART_tx_port				GPIOB
#define UART_tx_pin				GPIO_PIN_10
#define UART_rx_port				GPIOB
#define UART_rx_pin				GPIO_PIN_11
#define UART_irq					USART2_IRQn
#define UART_clk					RCU_USART2
#define UART_tx_gpio_clk			RCU_GPIOB
#define UART_rx_gpio_clk			RCU_GPIOB
#define UART_tx_dma				DMA0
#define UART_tx_dma_ch			DMA_CH1
#define UART_tx_dma_clk			RCU_DMA0
#define UART_rx_dma				DMA0
#define UART_rx_dma_ch			DMA_CH2
#define UART_rx_dma_clk			RCU_DMA0
#define UART_DMA_TX_IRQ          DMA0_Channel1_IRQn
#define UART_DMA_IRQHandler      DMA0_Channel1_IRQHandler
#endif

// ================================================================================
static hw_call_back rx_cb_;
static void* ctx_;
static c_buffer_t *tx_queue_;
static u8 rx_cache[CONFIG_UART_RX_BUFFER_SIZE];
static u16 tx_length, rx_index;

static void uart_pin_init(void){
	rcu_periph_clock_enable(UART_clk);
	rcu_periph_clock_enable(UART_rx_gpio_clk);
	rcu_periph_clock_enable(UART_tx_gpio_clk);
	rcu_periph_clock_enable(RCU_AF);
	gpio_init(UART_tx_port, GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,UART_tx_pin);
	gpio_init(UART_rx_port, GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,UART_rx_pin);
}

static void uart_device_init(u32 baud){
	usart_deinit(UART_com);
	usart_baudrate_set(UART_com, baud);

	usart_word_length_set(UART_com, USART_WL_8BIT);
	usart_stop_bit_set(UART_com, USART_STB_1BIT);
	usart_parity_config(UART_com, USART_PM_NONE);
	usart_hardware_flow_rts_config(UART_com, USART_RTS_DISABLE);
	usart_hardware_flow_cts_config(UART_com, USART_CTS_DISABLE);
	usart_receive_config(UART_com, USART_RECEIVE_ENABLE);
	usart_transmit_config(UART_com, USART_TRANSMIT_ENABLE);
}

static void uart_tx_dma_init(void){
	dma_parameter_struct dma_init_struct;
	rcu_periph_clock_enable(UART_tx_dma_clk);
	dma_deinit(UART_tx_dma, UART_tx_dma_ch);
	dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
	dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
	dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
	dma_init_struct.periph_addr = (u32) &USART_DATA(UART_com);
	dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
	dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
	dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
	dma_init(UART_tx_dma, UART_tx_dma_ch, &dma_init_struct);
	dma_circulation_disable(UART_tx_dma, UART_tx_dma_ch);
	dma_memory_to_memory_disable(UART_tx_dma, UART_tx_dma_ch);
	usart_dma_transmit_config(UART_com, USART_DENT_ENABLE);
}

static void uart_rx_dma_init(void){
	dma_parameter_struct dma_init_struct;
	rcu_periph_clock_enable(UART_rx_dma_clk);
	dma_deinit(UART_rx_dma, UART_rx_dma_ch);
	dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
	dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
	dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
	dma_init_struct.memory_addr = (u32)rx_cache;
	dma_init_struct.number = CONFIG_UART_RX_BUFFER_SIZE;
	dma_init_struct.periph_addr = (u32) &USART_DATA(UART_com);
	dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
	dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
	dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
	dma_init(UART_rx_dma, UART_rx_dma_ch, &dma_init_struct);
	dma_circulation_enable(UART_rx_dma, UART_rx_dma_ch);
	dma_memory_to_memory_disable(UART_rx_dma, UART_rx_dma_ch);
	dma_channel_enable(UART_rx_dma, UART_rx_dma_ch);
	usart_dma_receive_config(UART_com, USART_DENR_ENABLE);
}


void uart_init(unsigned char hw_id, u32 baud, hw_call_back cb, void *ctx) {
	rx_cb_ = cb;
	ctx_ = ctx;
	uart_pin_init();
	uart_device_init(baud);
	uart_tx_dma_init();
	uart_rx_dma_init();
	usart_enable(UART_com);
}


void uart_tx_poll(unsigned char hw_id)
{
	u32 value = DMA_CHCTL(UART_tx_dma, UART_tx_dma_ch);

	if (value & DMA_CHXCTL_CHEN) {
		if (SET != dma_flag_get(UART_tx_dma, UART_tx_dma_ch, DMA_FLAG_FTF)) {
			return;
		}
		dma_flag_clear(UART_tx_dma, UART_tx_dma_ch, DMA_FLAG_FTF);
		circle_skip_read_size(tx_queue_, tx_length);
		DMA_CHCTL(UART_tx_dma, UART_tx_dma_ch) = value & (~DMA_CHXCTL_CHEN);
	}

	tx_length = circle_get_read_size(tx_queue_);
	if (tx_length > 0) {
		DMA_CHCNT(UART_tx_dma, UART_tx_dma_ch) = tx_length;
		DMA_CHMADDR(UART_tx_dma, UART_tx_dma_ch) = (u32) circle_get_read_head(tx_queue_);
		DMA_CHCTL(UART_tx_dma, UART_tx_dma_ch) = value | DMA_CHXCTL_CHEN;
	}
}


void uart_rx_poll(unsigned char hw_id)
{
	u16 index = rx_index;

	rx_index = sizeof(rx_cache) - DMA_CHCNT(UART_rx_dma, UART_rx_dma_ch);
	if (rx_cb_) {
		if (rx_index < index) {
			rx_cb_(ctx_, rx_cache + index, sizeof(rx_cache) - index);
			rx_cb_(ctx_, rx_cache, rx_index);
		} else if (rx_index > index){
			rx_cb_(ctx_, rx_cache + index, rx_index - index);
		}
	}
}


void uart_set_dma_queue(unsigned char hw_id, void *queue) {
	tx_queue_ = (c_buffer_t *)queue;
}

